import Pyro.core
import Pyro.naming
import Pyro.errors
import sys
import os
import logging
from threading import *


#A maximum of 2000 concurrent connection
#is allowed for this version of pyOODSM
Pyro.config.PYRO_MAXCONNECTIONS = 2000

class CentralServer(Pyro.core.ObjBase):

    #Constructor that sets up the servers lists and locks.
    def __init__(self, daemon):
        logging.debug("DSMMaster is being initialized")
        self.daemon = daemon
        self.ObjectDB = {}
        self.ObjectHosts = {}
        self.RegisterHostLock = Lock()
        self.RegisterObjLock = Lock()
        logging.debug("DSMmaster initialized")
        return Pyro.core.ObjBase.__init__(self)


    #Destructor that shutsdown the pyOODSM network
    def __del__(self):
        logging.debug("DSMmaster is being destructed")
        for k in self.ObjectHosts.keys():
            self.ObjectHosts[k].serverKilled()
        self.ObjectHosts = {}
        self.ObjectDB = {}
        logging.debug("DSMmaster is destructed")



    #Function that registers the host. 
    #This is called from the clients.  
    def registerHost(self, host):
        logging.debug("Registration of host " + host + " Requested") 
        self.RegisterHostLock.acquire()
        if not self.ObjectHosts.has_key(host):
            self.ObjectHosts[host]=Pyro.core.getProxyForURI("PYRONAME://ODB"+host)
            logging.debug("Host registred: " + host)
            self.RegisterHostLock.release()
            return True
        else:
            logging.debug("Host " + host + " already exists")
            self.RegisterHostLock.release()
            return False



    #Function that registers the host. 
    #This is called from the clients. 
    def unRegisterHost(self, host):
        logging.debug("Unregistration of host " + host + " Requested")
        if self.ObejctHosts.has_key(host):
            del self.ObjectHosts[host]
            #print "Host list:"
            #print ObjectHosts
            logging.debug("Unregistration of host " + host + " Finnished")
            return True
       
        else:
            return False

        

    #Function for registering af an object in the server.
    #If the host, that has the object is unknown, it is added to
    #the servers list This is described in section 7.3.2 in the report.
    def register(self, name, host):
        logging.debug("Registration of object " + name + " on host " + host + " requested")
        self.RegisterObjLock.acquire()
        if self.ObjectDB.has_key(name):
	    logging.info("Object already exists with name: " + name)
            self.RegisterObjLock.release()
            #print "self.ObjectHosts"
            #print self.ObjectHosts
            remotehost = self.ObjectDB[name]
            return self.ObjectHosts[remotehost]
        else:
            self.ObjectDB[name]=host
            if not self.ObjectHosts.has_key(host):
                self.ObjectHosts[host]=Pyro.core.getProxyForURI("PYRONAME://ODB"+host)
            logging.debug("Object " + name + "resistered on host " + host)
            self.RegisterObjLock.release()
            return True




     #This function is called remotely from a client, that has an object, and
    #want's to remove it from the DSM network. The function removes the object
    #from alle hosts, and then removes it from the reference to the object,
    #from the server it self. This is described in section 7.3.3 in the report.
    def unRegisterObject(self, name, host):
        logging.debug("Unregistation of object " + name + " requested")
        if not self.ObjectDB.has_key(name):
            logging.debug("Unregistration of object " + name + "finished, object did'tn exists")
            return False
        else:
            for k in self.ObjectHosts.keys():
                try:
                    if not k == host:
                        self.ObjectHosts[k].unRegisterRemoteObject(name)
                except:
                    pass
            del self.ObjectDB[name]
            logging.debug("Unregistation of object " + name + " finished")
            return True



     #Function that shutsdown the server, on a remote call
    #All clients connected to the server is given a notice
    #that the server is no longer available. This is described in section 
    #3.5.8 in the report.
    def killServer(self, host):
        logging.debug("killServer called from " + host)
        for k in self.ObjectHosts.keys():
            #print k
            if k != host:
                try:
                    self.ObjectHosts[k].serverKilled()
                except Pyro.errors.ConnectionClosedError, e:
                    pass
            del(self.ObjectHosts[k])
        #os._exit(0)
        logging.debug("killServer completet")
        self.daemon.shutdown(True)
        sys.exit()










#Function which starts a PYRO server, looking up a PYRO nameserver
#and starts the PYRO request loop.
def DSMmaster():        
    try:
        daemon=Pyro.core.Daemon()
        locator = Pyro.naming.NameServerLocator()
        ns = locator.getNS()
        daemon.useNameServer(ns)
        uri=daemon.connect(CentralServer(daemon),"DSM_NS")
        daemon.requestLoop()
    finally:
        logging.info("DSMmaster is shutting down.")
        daemon.shutdown(True)
        sys.exit()


#The server is started, when the script is executed.
logLevel = sys.argv[1]
logFile = sys.argv[2]

if logLevel == "debug":
    logLevel = logging.DEBUG
if logLevel == "info":
    logLevel = logging.INFO
if logLevel == "error":
    logLevel = logging.ERROR
if logLevel == "warning":
    logLevel = logging.WARNING
if logLevel == "critical":
    logLevel = logging.CRITICAL

logging.basicConfig(level=logLevel, format='[%(asctime)s][%(levelname)s] %(message)s',filename=logFile, filemode='a')

master=DSMmaster()
